/*
    wifidb, version 0.01 alpha
    Copyright (C) 2010, Andrey Zhidenkov

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/ 

#include <stdio.h>
#include <iwlib.h>
#include <getopt.h>

#include "iwscan.h"

typedef struct iwscan_state
{
    int ap_num;
    int val_index;
} iwscan_state;

struct option long_options[] =
{
    {"scan", 0, NULL, 's'},
    {NULL, 0, NULL, 0}
};

/* 
 * Print scan result to standard output
 * 
 * This code mostly based on iwlist.c from wireless-tools,
 * thanks to Jean Tourrilnes <jt@hlp.hp.com>
 * Actually, we may not use *stream parameter (?)
 */

void
print_scan_result(struct stream_descr *stream,
                  struct iw_event * event,
                  struct iw_scan_state * state,
                  struct iw_range * iw_range,
                  int has_range)
{
    char buffer[128];

    switch(event->cmd)
    {
        case SIOCGIWAP:
            printf("MAC address: %s\n", iw_saether_ntop(&event->u.ap_addr, buffer));
            break;
        case SIOCGIWESSID:
            {
                char essid[IW_ESSID_MAX_SIZE+1];
                memset(essid, '\0', sizeof(essid));
                printf("essid length: %x\n", event->u.essid.length);
            }
            break;
        default:
            printf("cmd received: %02X\n", event->cmd);
    }
    return;
}

int
scan_iface(int skfd,        /* socket file descriptor */
           char *iface)     /* interface name (eth0, wlan0, etc.) */
{  
  /* definitions from wireless.h */ 
  struct iwreq     wrq;         /* defined in wireless22.h:951 */
  struct iw_scan_req scanopt;    /* defined in wireless22.h:772 */
  struct iw_range range;         /* defined in wireless22.h:972 */

  unsigned char *buffer = NULL; /* buffer for results */
  int buflen = IW_SCAN_MAX_DATA; /* 4096 */
  struct timeval tv;
  int has_range;
  int timeout = 15000000;

  /* Get range stuff */
  has_range = (iw_get_range_info(skfd, iface, &range) >= 0);

  /* Check if the interface could support scanning. */
  if((!has_range) || (range.we_version_compiled < 14))
  {
    fprintf(stderr, "%-8.16s  Interface doesn't support scanning.\n\n", iface);
    return(-1);
  }

  tv.tv_sec = 0;
  tv.tv_usec = 1000000;

  wrq.u.data.pointer = NULL;
  wrq.u.data.flags = 0;
  wrq.u.data.length = 0;
  /* Initiate scanning */
  if (iw_set_ext(skfd, iface, SIOCSIWSCAN, &wrq) < 0)
  {  
    if(errno != EPERM)
    {
      fprintf(stderr, "Interface %s doesn't support scanning: %s\n\n", iface, strerror(errno));
      exit(-1);
    }
    tv.tv_usec = 0;
  } 
  timeout -= tv.tv_usec;
  
  while(1)
  {
    fd_set rfds;
  
    int last_fd;
    int ret;   

    FD_ZERO(&rfds);
    last_fd = -1;  

    ret = select(last_fd + 1, &rfds, NULL, NULL, &tv);
    printf("ret = %d\n", ret);
    if (ret < 0)
    {
      if (errno == EAGAIN || errno == EINTR)
        continue;
      fprintf(stderr, "Unhandled signal, exiting...\n");
      exit(-1);
    }
    if (ret == 0)
    {
      unsigned char *newbuf;

      realloc:
      
      newbuf = realloc(buffer, buflen);
      if (newbuf == NULL)
      {
        fprintf(stderr, "Failed to allocate memory for buffer\n");
        exit(-1);
      }
      buffer = newbuf;
      wrq.u.data.pointer = buffer;
      wrq.u.data.flags = 0;
      wrq.u.data.length = buflen;

      if (iw_get_ext(skfd, iface, SIOCGIWSCAN, &wrq) < 0)
      {
        printf("iw_get_ext() < 0\n");
        if (wrq.u.data.length > buflen)
          buflen = wrq.u.data.length;
        else
          buflen = buflen * 2;

        goto realloc;
      }

      fprintf(stderr, "Failed to read scan data (errno = %d): %s\n", errno, strerror(errno));
      exit(-1);
    } /* ret == 0 */
    else
      break;
  }
  /* read the results */
  if (wrq.u.data.length)
  {
    struct iw_event		iwe;
    struct stream_descr	stream;
    struct iwscan_state	state = { .ap_num = 1, .val_index = 0 };
    int			ret;

    printf("Scan completed\n");
    
    iw_init_event_stream(&stream, (char *) buffer, wrq.u.data.length);

    do
    {
      ret = iw_extract_event_stream(&stream, &iwe, &range.we_version_compiled);
      if (ret > 0)
      {
        print_scan_result(&stream, &iwe, &state, &range, has_range);
      }
    } while (ret > 0);
    printf("\n");
  }
  else
    printf("No scan results\n");

  free(buffer);
  return 0;
}


void 
usage()
{
    char banner[100];

    sprintf(banner, "iwscan, version %s\n", VERSION);
    printf(banner);
}

/*
 Original iw_get_range_info() does a lot of job, so
 I've decided to write my ow function. It does only
 nessecary things
*/

int
wf_iface_can_scan(int skdf,
                  char *iface)
{
    return 0;
}

int
main(int argc, char *argv[])
{
    int i, option_index;
    const char * iface = "eth1"; /* now we'll store an interface name here, later it will be read from params */

    while ((i = getopt_long(argc, argv, "s:h", long_options, &option_index)) != -1)
    {
        switch(i)
        {
            case 's':
              {
                int skfd;
                if((skfd = iw_sockets_open()) < 0)
                {
                  fprintf(stderr, "Cannot open socket\n");
                }
                scan_iface(skfd, iface);
              }
                break;
            case 'h':
            default:
                usage();
                exit(0);
        }
    }
    return 0;
}
